﻿#include "byte_buffer.h"
#include <iostream>
#include <sstream>

#ifdef _WIN32
#include <assert.h>
#else
#include <assert.h>
#include <string.h>
void memmove_s(void *dest, int len, void *src, int slen)
{
	memmove(dest, src, len);
}
#define sprintf_s sprintf
#endif

#define BUF_RESIZE_PER 1.4

ByteBuffer::ByteBuffer(uint32 bufferSize /* = DEFAULT_SIZE */)
	: wPos_(0), rPos_(0)
{
	buf_.reserve(bufferSize);
}

ByteBuffer::ByteBuffer(uint8 *data, uint32 bufLen)
	: wPos_(0), rPos_(0)
{
	buf_.reserve(bufLen);
	write_bytes(data, bufLen);
}

ByteBuffer::ByteBuffer(ByteBuffer &buf)
	: wPos_(buf.wPos_), rPos_(buf.rPos_), buf_(buf.buf_), name_(buf.name_)
{

}

ByteBuffer::ByteBuffer(ByteBuffer &&buf)noexcept
	: wPos_(buf.wPos_), rPos_(buf.rPos_), buf_(std::move(buf.buf_)), name_(std::move(buf.name_))
{

}

ByteBuffer& ByteBuffer::operator=(const ByteBuffer &buf)
{
	buf_ = buf.buf_;
	wPos_ = buf.wPos_;
	rPos_ = buf.rPos_;
	name_ = buf.name_;
	return *this;
}

ByteBuffer& ByteBuffer::operator=(ByteBuffer &&buf)noexcept
{
	buf_ = std::move(buf.buf_);
	name_ = std::move(buf.name_);
	wPos_ = buf.wPos_;
	rPos_ = buf.rPos_;

	buf.wPos_ = 0;
	buf.rPos_ = 0;
	return *this;
}

const uint8& ByteBuffer::operator[](const int pos)const
{
	assert(pos < buf_.size());
	return buf_[pos];
}

void ByteBuffer::swap(ByteBuffer &buf)
{
	buf_.swap(buf.buf_);
	std::swap(wPos_, buf.wPos_);
	std::swap(rPos_, buf.rPos_);
	name_.swap(buf.name_);
}

void ByteBuffer::setName(const char *s)
{
	name_ = s;
}

void ByteBuffer::reset()
{
	wPos_ = 0;
	rPos_ = 0;
	buf_.reserve(DEFAULT_SIZE);
	name_.clear();
}

int ByteBuffer::size()
{
	return static_cast<int>(buf_.size());
}

void ByteBuffer::make_writeable_bytes(size_t bytes)
{
	if (bytes > WriteableBytes())
	{
		buf_.resize((GetWritePos() + bytes) * BUF_RESIZE_PER);
	}
}

void ByteBuffer::reserve(size_t bytes)
{
	int newSize = (int)bytes + wPos_;
	if (buf_.capacity() < newSize)
	{
		if (newSize < 100)
			buf_.reserve(300);
		else if (newSize < 750)
			buf_.reserve(2500);
		else if (newSize < 6000)
			buf_.reserve(10000);
		else
			buf_.reserve(400000);
	}
}

void ByteBuffer::gc()
{
	int useCount = std::min(wPos_, rPos_);
	if (useCount == 0)
	{
		wPos_ = rPos_ = 0;
		return;
	}

	if (wPos_ > rPos_)
	{
		int notUseCount = ReadableBytes();
		memmove_s(&buf_[0], notUseCount, &buf_[rPos_], notUseCount);
		rPos_ = 0;
		wPos_ = notUseCount;
		buf_.resize(notUseCount);
	}
	else if (wPos_ == rPos_)
	{
		wPos_ = 0;
		rPos_ = 0;
		buf_.resize(useCount);
	}
}

int ByteBuffer::ReadableBytes()const
{
	return wPos_ - rPos_;
}

int ByteBuffer::WriteableBytes()const
{
	int wsize = static_cast<int>(buf_.size());
	if (wsize < wPos_)
		return 0;
	return wsize - wPos_;
}

bool ByteBuffer::CanRead(int s)
{
	return ReadableBytes() >= s;
}

bool ByteBuffer::CanWrite(int s)
{
	return WriteableBytes() >= s;
}

uint8* ByteBuffer::BeginWritePtr()
{
	return begin();
}

const uint8* ByteBuffer::BeginWriteConstPtr()
{
	return BeginWritePtr();
}

uint8* ByteBuffer::GetWritePtr()
{
	return begin() + wPos_;
}

uint8* ByteBuffer::GetReadPtr()
{
	return begin() + rPos_;
}

int ByteBuffer::AddWritePos(int n)
{
	return wPos_ += n;
}

int ByteBuffer::AddReadPos(int n)
{
	return rPos_ += n;
}

int ByteBuffer::SetWritePos(int n)
{
	return wPos_ = n;
}

int ByteBuffer::SetReadPos(int n)
{
	return rPos_ = n;
}

int ByteBuffer::GetWritePos()
{
	return wPos_;
}

int ByteBuffer::GetReadPos()
{
	return rPos_;
}

int ByteBuffer::write_int8(const int8 &v)
{
	append<int8>(v);
	return 0;
}

int ByteBuffer::write_int16(const int16 &v)
{
#ifdef USE_BIG_ENDIAN
	append<int16>(as::swap16(v));
#else
	append<int16>(v);
#endif
	return 0;
}

int ByteBuffer::write_int32(const int32 &v)
{
#ifdef USE_BIG_ENDIAN
	append<int32>(as::swap32(v));
#else
	append<int32>(v);
#endif
	return 0;
}

int ByteBuffer::write_int64(const int64 &v)
{
#ifdef USE_BIG_ENDIAN
	append<int64>(as::swap64(v));
#else
	append<int64>(v);
#endif
	return 0;
}

int ByteBuffer::write_float(const float &v)
{
#ifdef USE_BIG_ENDIAN
	append<float>(as::swapfloat(v));
#else
	append<float>(v);
#endif
	return 0;
}

int ByteBuffer::write_double(const double &v)
{
#ifdef USE_BIG_ENDIAN
	append<double>(as::swapdouble(v));
#else
	append<double>(v);
#endif
	return 0;
}

int ByteBuffer::write_uint8(const uint8 &v)
{
	append<uint8>(v);
	return 0;
}

int ByteBuffer::write_uint16(const uint16 &v)
{
#ifdef USE_BIG_ENDIAN
	append<uint16>(as::swap16(v));
#else
	append<uint16>(v);
#endif
	return 0;
}

int ByteBuffer::write_uint32(const uint32 &v)
{
#ifdef USE_BIG_ENDIAN
	append<uint32>(as::swap32(v));
#else
	append<uint32>(v);
#endif
	return 0;
}

int ByteBuffer::write_uint64(const uint64 &v)
{
#ifdef USE_BIG_ENDIAN
	append<uint64>(as::swap64(v));
#else
	append<uint64>(v);
#endif
	return 0;
}

int ByteBuffer::write_string(const std::string &v)
{
	uint16 len = static_cast<uint16>(v.length());
	write_uint16(len);
	if (check_read_string_size(len) == false)
		return -1;
	write_bytes((const uint8*)v.c_str(), len);
	return 0;
}

int ByteBuffer::write_string(const std::string_view &v)
{
	uint16 len = static_cast<uint16>(v.length());
	write_uint16(len);

	if (check_read_string_size(len) == false)
		return -1;

	write_bytes((const uint8 *)v.data(), len);
	return 0;
}

int ByteBuffer::write_string(const char *v, int len)
{
	uint16 plen = len == 0 ? static_cast<uint16>(strlen(v)) : len;
	write_uint16(plen);

	if (check_read_string_size(plen) == false)
		return -1;

	write_bytes((const uint8 *)v, plen);
	return 0;
}

int ByteBuffer::write_bytes(const uint8 *b, uint32 len)
{
	if (b == nullptr)
		return 0;

	if (len == 0)
		return 0;

	append(*b, len);
	return 0;
}

int ByteBuffer::read_int8(int8 &v)
{
	v = read<int8>();
	return 0;
}

int ByteBuffer::read_int16(int16 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap16(read<int16>());
#else
	v = read<int16>();
#endif
	return 0;
}

int ByteBuffer::read_int32(int32 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap32(read<int32>());
#else
	v = read<int32>();
#endif
	return 0;
}

int ByteBuffer::read_int64(int64 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap64(read<int64>());
#else
	v = read<int64>();
#endif
	return 0;
}

int ByteBuffer::read_float(float &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swapfloat(read<float>());
#else
	v = read<float>();
#endif
	return 0;
}

int ByteBuffer::read_double(double &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swapdouble(read<double>());
#else
	v = read<double>();
#endif
	return 0;
}

int ByteBuffer::read_uint8(uint8 &v)
{
	v = read<uint8>();
	return 0;
}

int ByteBuffer::read_uint16(uint16 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap16(read<uint16>());
#else
	v = read<uint16>();
#endif
	return 0;
}

int ByteBuffer::read_uint32(uint32 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap32(read<uint32>());
#else
	v = read<uint32>();
#endif
	return 0;
}

int ByteBuffer::read_uint64(uint64 &v)
{
#ifdef USE_BIG_ENDIAN
	v = as::swap64(read<uint64>());
#else
	v = read<uint64>();
#endif
	return 0;
}

int ByteBuffer::read_string(std::string &v)
{
	uint16 len = 0;
	read_uint16(len);
	if (check_read_string_size(len) == false)
		return -1;
	if (ReadableBytes() < len)
		return -1;
	v.append((char *)GetReadPtr(), len);
	AddReadPos(len);
	return len;
}

int ByteBuffer::read_string(char *buf, int len)
{
	uint16 plen = 0;
	read_uint16(plen);
	plen = std::min<uint16>(plen, len);
	if (check_read_string_size(plen) == false)
		return -1;
	if (ReadableBytes() < plen)
		return -1;

	memcpy(buf, GetReadPtr(), plen);
	AddReadPos(plen);
	return plen;
}

int ByteBuffer::read_bytes(uint8 *v, int len)
{
	return read(*v, len);
}

int ByteBuffer::read_bytes(std::vector<uint8> &buf, int len)
{
	if (ReadableBytes() < len)
		return -1;

	uint8 *ptr = GetReadPtr();
	for (int i = 0; i < len; ++i)
	{
		buf.push_back(ptr[i]);
	}
	AddReadPos(len);
	return 0;
}

bool ByteBuffer::check_read_string_size(int len)
{
	if (len <= 0 || len >= MAX_READ_STRING_LEN)
	{
		return false;
	}
	return true;
}

uint8* ByteBuffer::begin()
{
	if (buf_.size() <= 0)
		return buf_.data();
	return &*buf_.begin();
}

const uint8 *ByteBuffer::constbegin()
{
	if (buf_.size() <= 0)
		return nullptr;
	return &*buf_.begin();
}

void ByteBuffer::printInfo() {
	uint32 length = static_cast<uint32>(buf_.size());
	std::cout << "ByteBuffer " << name_.c_str() << " Length: " << length << ". Info Print" << std::endl;
}

void ByteBuffer::printAH() {
	uint32 length = static_cast<uint32>(buf_.size());
	std::cout << "ByteBuffer " << name_.c_str() << " Length: " << length << ". ASCII & Hex Print" << std::endl;

	for (uint32 i = 0; i < length; i++) {
		std::printf("0x%02x ", buf_[i]);
	}

	std::printf("\n");
	for (uint32 i = 0; i < length; i++) {
		std::printf("%c ", buf_[i]);
	}

	std::printf("\n");
}

void ByteBuffer::printAscii() {
	uint32 length = static_cast<uint32>(buf_.size());
	std::cout << "ByteBuffer " << name_.c_str() << " Length: " << length << ". ASCII Print" << std::endl;

	for (uint32 i = 0; i < length; i++) {
		std::printf("%c ", buf_[i]);
	}

	std::printf("\n");
}

void ByteBuffer::printHex(bool isRead) {

	uint32 length = ReadableBytes();
	if (isRead == false)
		length = WriteableBytes();
	length = std::min<uint32>(length, 500);
	std::cout << "\nByteBuffer " << name_.c_str() << " Length: " << length << ". Hex Print" << std::endl;

	for (uint32 i = 0; i < length; i++) {
		std::printf("0x%02x ", buf_[i]);
	}

	std::printf("\n");
}

void ByteBuffer::printPosition() {
	uint32 length = static_cast<uint32>(buf_.size());
	std::cout << "ByteBuffer " << name_.c_str() << " Length: " << length << " Read Pos: " << rPos_ << ". Write Pos: "
		<< wPos_ << std::endl;
}

std::string ByteBuffer::getHex(bool isRead)
{
	uint32 length = ReadableBytes();
	if (isRead == false)
		length = WriteableBytes();
	length = std::min<uint32>(length, 500);
	std::stringstream ss;
	char hex[10];
	for (int i = 0; i < (int)length; ++i)
	{
		memset(hex, 0, sizeof(hex));
		sprintf_s(hex, "0x%02x ", buf_[i]);
		ss << hex;
	}

	return ss.str();
}
